package fr.emse.tatiana.display;

import java.util.*;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.w3c.dom.Node;

import fr.emse.tatiana.replayable.Anchor;
import fr.emse.tatiana.replayable.IReplayableModel;
import fr.emse.tatiana.replayable.SynchronisationEvent;
import fr.emse.tatiana.replayable.analysis.AnalysisEvent;
import fr.emse.tatiana.replayable.analysis.IAnalysis;

public class ReplayableDisplayShower extends DisplayShower implements IReplayableModel {

	private List<Long> eventList;	
	private List<IAnalysis> analyses;
	private int timecol;
	private int anchorcol;
	private List<Long> durationList;

	public ReplayableDisplayShower(DisplayTable dtable,List<IAnalysis> analyses) {
		super(dtable);
		this.analyses=analyses;
		timecol = dt.getColumn("Time"); // could be in any of these three
		if (timecol<0) timecol = dt.getColumn("Date");
		if (timecol<0) timecol = dt.getColumn("time"); 
		anchorcol = dt.getColumn("Source Anchor");
		if (anchorcol<0) anchorcol = dt.getColumn("src-anchor"); 
	}

	/* getEvents also initiates the fact that the remote is "active"
	 * @see fr.emse.tatiana.DiscreteReplayable#getEvents()
	 */
	public List<Long> getEventTimestamps() {
		if (eventList==null) {
			eventList = new ArrayList<Long>(); 
			durationList = new ArrayList<Long>();
			for (int i=0; i<dt.rownb; i++) {
				try {
					eventList.add( dt.getDateValue(i,timecol).getTime());
					durationList.add(dt.getDateValue(i,timecol).getDuration());
				} catch (Exception e) {
					eventList.add(new Long(-1));
					durationList.add(new Long(0));
				}
			}
		}
		return eventList;
	}

	public void goTo(long time) {
		// do nothing
	}


	public void mark(long time) {

	}


	public int timeToIndex(long time) {
		Long t = new Long(time);
		int first = eventList.indexOf(t);
		if (first<0) { // doesn't exactly match an event
			for (int i = 0; i<eventList.size() && (time>eventList.get(i));i++) {
				first=i;
			}
		}

		int last = first;

		if (first>=0) {			
			try {
				while (t.equals(eventList.get(last+1))) {
					last++;
				} 
			} catch (IndexOutOfBoundsException e) {
				// not a problem
			}
		}
		return last;

	}

	public int getFacetCount() {
		return dtm.getColumnCount()+analyses.size();
	}


	public int getPreferredWidth(int col) {
		if (col<dtm.getColumnCount()) {
			return (Integer) dt.aux[DisplayTable.CHWidth][col];
		} else {
			return 10; // for analysis columns
		}
	}


	public int getFontSize() {
		return dt.fsize;
	}


	public String getFacetName(int col) {
		if (col<dtm.getColumnCount()) {
			return dtm.getColumnName(col);
		} else {
			return analyses.get(col-dtm.getColumnCount()).getName(); // for analysis columns
		}
	}

	public int getFacetCol(String name) {
		for (int col=0;col<dtm.getColumnCount(); col++) {
			if (dtm.getColumnName(col).equals(name)) {
				return col;
			}
		}
		for (int i=0; i<analyses.size(); i++) {
			if (analyses.get(i).getName().equals(name)) {
				return dtm.getColumnCount()+i;
			}
		}
		return -1;
	}

	public Object getFacet(Object elem, int columnIndex) {
		int row = (Integer) elem;
		if (columnIndex<dtm.getColumnCount()) {
			return dt.data[row][columnIndex];
		} else {
			return analyses.get(columnIndex-dtm.getColumnCount()).getValueForAnchor(new Anchor((Node) dt.data[row][anchorcol])); // for analysis columns
		}
	}


	public CellEditor getCellEditor(int col, Composite table) {
		if (col<dtm.getColumnCount()) {
			return null;
		} else {
			return analyses.get(col-dtm.getColumnCount()).getChooser(table); // for analysis columns
		}
	}


	public boolean canModifyColumn(int col) {
		return (col<dtm.getColumnCount())?false:true;
	}


	public void setFacetValue(Object row, int col, Object value) {
		if (col<dtm.getColumnCount()) {
			// do nothing
		} else {
			analyses.get(col-dtm.getColumnCount()).setValue(getAnchor(row),value);
		}
	}

	public Anchor getAnchor(Object elem) {
		if (anchorcol < 0) {
			return new Anchor("none");
		}
		return new Anchor((Node) dt.data[(Integer) elem][anchorcol]);
	}

	public Object[] getAnalysedEvents(Set<Anchor> anchors) {
		Vector<Integer> rows = new Vector<Integer>();
		for (int row=0; row < this.getEventCount(); row++) {
			if (anchors.contains(getAnchor(row))) {
				rows.add(new Integer(row));
			}
		}
		return rows.toArray(new Integer[rows.size()]);
	}

	public Object[] getAnalysedEvents(IAnalysis a) {
		return getAnalysedEvents(a.getAnchors());
	}


	public Object[] getAnalysedEvents(AnalysisEvent evt) {
		Set<Anchor> anchors = new HashSet<Anchor>();
		anchors.addAll(evt.getAnchors());
		return getAnalysedEvents(anchors);
	}


	public String getFacetText(Object elem, int col) {
		int row = (Integer) elem;
		if (col<dtm.getColumnCount()) {
			try {
				Object val = getFacet(row,col);
				String ret = val.toString();
				if (val instanceof Date || val instanceof DisplayDate) {
					if (true) {
						ret = ret.substring(11,19);
					}
				} else {
					try {
						Node node = (Node) val;
						ret = Ut.formatXMLNode(node);
					} catch (Exception e) {
						// wasn't a node
					}
				}
				return ret;
			} catch (Exception e) {
				return null;
			}
		} else {
			return analyses.get(col-dtm.getColumnCount()).getTextValueForAnchor(getAnchor(row));
		}
	}


	public int getFacetIdFor(IAnalysis a) {
		int ret = analyses.indexOf(a);
		if (ret < 0)
			return ret;
		else
			return dtm.getColumnCount() + ret;
	}


	public Color getBackground(Object elem, int col) {
		if (col<dtm.getColumnCount()) {
			return null;
		} else {
			return analyses.get(col-dtm.getColumnCount()).getBackground(getAnchor(elem));
		}
	}


	public Color getForeground(Object elem, int col) {
		Object facet = getFacet(elem, col);
		if (facet instanceof DisplayCell) {
			return new Color(null,Ut.toSWTRGB(((DisplayCell) facet).fc));
		}
		return null;
	}


	public List<IAnalysis> getAnalyses() {
		return analyses;
	}


	public Object lastEventBefore(long time) {
		int idx = timeToIndex(time);
		if (idx >= 0 && idx < getEventCount()) {
			return new Integer(idx);
		}
		return null;
	}

	public List<Object> currentEvents(SynchronisationEvent event) {

		Vector<Object> elems = new Vector<Object>();

		long begin = event.getBegin();
		long end = begin + event.getDuration();

		for (int i=0; i<getEventCount() && getBeginTime(new Integer(i)) <= end; i++) {
			Integer elem = new Integer(i);
			if ((getBeginTime(elem)+getDuration(elem)) >= begin) {
				elems.add(elem);
			}
		}

		if (elems.isEmpty()) {
			int limit = timeToIndex(event.getNow());
			if (limit < 0) limit = 0;
			if (limit >= getEventCount()) limit = getEventCount()-1;
			elems.add(new Integer(limit)); //(at least one, even if it don't count)
		}
		return elems;
	}


	public Object getEvent(int i) {
		return new Integer(i);
	}

	public int getEventCount() {
		return getEventTimestamps().size();
	}


	public void doSave() {
		// TODO Auto-generated method stub

	}


	public void doSave(String value) {
		// TODO Auto-generated method stub

	}


	public boolean hasReplayableFile() {
		// TODO Auto-generated method stub
		return false;
	}


	public long getBeginTime(Object elem) {
		return getEventTimestamps().get((Integer) elem);
	}

	private long getDuration(Object elem) {
		return durationList.get((Integer) elem);
	}


	public long getLastTime(Object elem) {
		return getBeginTime(elem)+getDuration(elem);
	}


	public List<Object> anchoredEvents(SynchronisationEvent event) {
		Vector<Object> elems = new Vector<Object>();
		for (int i = 0; i<getEventCount(); i++) {
			Anchor eventa = getAnchor(new Integer(i));
			for (Anchor a : event.getAnchors()) {
				if (a.subOrSuperSet(eventa)) {
					elems.add(new Integer(i));
				}
			}
		}
		return elems;
	}

	public long getFirstTime() {
		return getEventTimestamps().get(0);
	}

	public long getLastTime() {
		return getEventTimestamps().get(getEventTimestamps().size()-1);
	}

	public boolean isAnalysisFacet(int col) {
		return ! (col<dtm.getColumnCount());
	}

}